//
// Created by XUAN on 2024/8/25.
//

#include "webConfigServer.h"
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "cJSON.h"

#define BUFF_SIZE 3072
#define DEFAULT_SCAN_LIST_SIZE 20

static const char *TAG = "WEB_CONFIG_SERVER";

/*---------------------------blufi_app---------------------*/
extern char* scan_wifi_list;
/*--------------------------------------------------------*/
/*---------------------------parameters------------------------------*/
TaskHandle_t webServerTaskHandle = NULL;
TaskHandle_t webSocketTaskHandle = NULL;
volatile bool webSocketTaskShouldExit = false;

/*-------------------------------------------------------------------*/
/* Initialize Wi-Fi as STA and set scan method */
void wifi_scan(void)
{
    esp_wifi_scan_start(NULL, true);
}

extern const uint8_t index_html_start[] asm("_binary_index_html_start");
extern const uint8_t index_html_end[] asm("_binary_index_html_end");

static const char *HTTP_200 = "HTTP/1.1 200 OK\r\n"
                              "Server: lwIP/1.4.0\r\n"
                              "Content-Type: text/html\r\n"
                              "Connection: Keep-Alive\r\n"
                              "Content-Length: %d \r\n\r\n";
static const char *HTTP_200_json = "HTTP/1.1 200 OK\r\n"
                                   "Server: lwIP/1.4.0\r\n"
                                   "Content-Type: application/json\r\n"
                                   "Connection: Keep-Alive\r\n"
                                   "Content-Length: %d \r\n\r\n";

static const char *HTTP_400 = "HTTP/1.0 400 BadRequest\r\n"
                              "Content-Length: 0\r\n"
                              "Connection: Close\r\n"
                              "Server: lwIP/1.4.0\r\n\n";

int my_write(int fd, void *buffer, int length)
{
    int bytes_left = length;
    int written_bytes;
    char *ptr = buffer;

    while (bytes_left > 0)
    {
        written_bytes = send(fd, ptr, bytes_left, 0);
        if (written_bytes <= 0)
        {
            if (errno == EINTR)
                written_bytes = 0;
            else
                return -1;
        }
        bytes_left -= written_bytes;
        ptr += written_bytes;
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    return 0;
}

static wifi_config_t sta_config;
char temp_buff[BUFF_SIZE] = {0};

void handle_http_request(void *pvParameters)
{
    char buff[BUFF_SIZE] = {0}; // 数据缓冲器
    int fd = *(int *)pvParameters;
    int bytes_recvd = 0;
    char *uri = NULL;

    ESP_LOGI(TAG, "Http Sub Task Run with socket: %d", fd);

    vTaskDelay(pdMS_TO_TICKS(30));
    while (1)
    {
     // 读取HTTP请求头
        bytes_recvd = recv(fd, buff, BUFF_SIZE - 1, 0);
        sprintf(temp_buff, "%s", buff);

        if (bytes_recvd <= 0)
        {
            ESP_LOGE(TAG, "Recv request header error!");
            goto request_error;
        }

        // 解析请求类型及请求URI
        uri = strstr(buff, "HTTP");
        if (uri == NULL)
        {
            ESP_LOGE(TAG, "Parse request header error!");
            goto request_error;
        }
        uri[0] = 0;
        uri = NULL;

        uri = strstr(buff, " ");
        if (uri == NULL)
        {
            ESP_LOGE(TAG, "Parse request URI error!");
            goto request_error;
        }
        uri[0] = 0;
        uri++;

        ESP_LOGI(TAG, "The request type is %s, URI is: %s", buff, uri);

        if (strcmp(buff, "GET") == 0) // 响应GET请求
        {
            if (strcmp(uri, "/wifilist ") == 0)
            {
                // 假设原始的 scan_wifi_list 是数组对象格式的 JSON 字符串
                char new_scan_wifi_list[1024];  // 这里需要确保有足够的空间来存放新的 JSON 字符串
                cJSON *wifi_array = cJSON_Parse(scan_wifi_list);
                cJSON *wifi_item;
                cJSON *new_wifi_object = cJSON_CreateObject();  // 创建一个新的 JSON 对象

                cJSON_ArrayForEach(wifi_item, wifi_array)
                {
                    cJSON *ssid = cJSON_GetObjectItem(wifi_item, "SSID");
                    cJSON *rssi = cJSON_GetObjectItem(wifi_item, "RSSI");
                    if (ssid && rssi)
                    {
                        // 将每一个 WiFi 的名字和对应的 RSSI 参数添加到新 JSON 对象中
                        cJSON_AddNumberToObject(new_wifi_object, ssid->valuestring, rssi->valueint);
                    }
                }

                // 将新的 JSON 对象转换为字符串
                char *new_scan_wifi_list_str = cJSON_PrintUnformatted(new_wifi_object);

                // 返回响应
                int length = sprintf(buff, HTTP_200_json, strlen(new_scan_wifi_list_str));
                my_write(fd, buff, length);
                my_write(fd, new_scan_wifi_list_str, strlen(new_scan_wifi_list_str));

                // 打印日志
                ESP_LOGI(TAG, "The request type is %s", new_scan_wifi_list_str);

                // 释放内存
                cJSON_Delete(wifi_array);
                cJSON_Delete(new_wifi_object);
                free(new_scan_wifi_list_str);
            }
            else
            {
                int length = sprintf(buff, HTTP_200, index_html_end - index_html_start);
                my_write(fd, buff, length);
                my_write(fd, index_html_start, index_html_end - index_html_start);
            }
        }
        else if (strcmp(buff, "POST") == 0) // 响应POST请求
        {
            char *temp_buff_ssid_pass = strchr(temp_buff, '{');
            if (temp_buff_ssid_pass != NULL)
            {
                cJSON *root = cJSON_Parse(temp_buff_ssid_pass);
                ESP_LOGI(TAG, "%s", temp_buff_ssid_pass);
                if (!root)
                {
                    ESP_LOGE(TAG, "JSON Parse Error: %s", cJSON_GetErrorPtr());
                }
                else
                {
                    const char *ssid = cJSON_GetObjectItem(root, "ssid")->valuestring;
                    const char *pass = cJSON_GetObjectItem(root, "passwd")->valuestring;

                    snprintf((char *)sta_config.sta.ssid, sizeof(sta_config.sta.ssid), "%s", ssid);
                    snprintf((char *)sta_config.sta.password, sizeof(sta_config.sta.password), "%s", pass);
                    sta_config.sta.bssid_set = 0;

                    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));

                    if (ESP_OK == esp_wifi_set_config(WIFI_IF_STA, &sta_config))
                        ESP_LOGI(TAG, "SET SUCCESS: SSID: %s, PASS: %s", ssid, pass);
                    else
                        ESP_LOGI(TAG, "SET FAIL: %s", esp_err_to_name(esp_wifi_set_config(WIFI_IF_STA, &sta_config)));
                    esp_wifi_connect();

                    cJSON_Delete(root);
                }
            }
        }
        else // 其他请求不响应
        {
            my_write(fd, HTTP_400, strlen(HTTP_400));
        }
        break;
    }

    vTaskDelay(pdMS_TO_TICKS(30));

    request_error:
    ESP_LOGI(TAG, "Close socket %d", fd);
    close(fd);
    vTaskDelete(NULL);
}

void webserver(void *pvParameters)
{
    int sockfd, new_fd;
    struct sockaddr_in my_addr = {0};
    struct sockaddr_in their_addr = {0};
    socklen_t sin_size = sizeof(struct sockaddr_in);

    struct timeval tv = {
            .tv_sec = 10,
            .tv_usec = 0,
    };

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        ESP_LOGE(TAG, "Socket creation failed: %d", errno);
        goto web_err;
    }

    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(80);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0)
    {
        ESP_LOGE(TAG, "Bind error");
        goto web_err;
    }

    listen(sockfd, 8);
    ESP_LOGI(TAG, "Webserver started...");

    while (1)
    {
        new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
        if (new_fd == -1)
        {
            ESP_LOGE(TAG, "Accept failed");
        }
        else
        {
            ESP_LOGI(TAG, "Accept new socket: %d", new_fd);

            setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
            setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));

            int *para_fd = malloc(sizeof(int));
            *para_fd = new_fd;
            xTaskCreate(&handle_http_request, "socket_task", 1024 * 10, para_fd, 6, &webSocketTaskHandle);
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }

    web_err:
    vTaskDelete(NULL);
}

void web_server_start(void)
{
    xTaskCreate(&webserver, "webserver_task", 3072 * 4, NULL, 5, &webServerTaskHandle);
}
